using System;
using System.Collections;
using System.Diagnostics;
using System.Xml.Serialization;
using Microsoft.SharePoint;
using System.Reflection;
using DE.Sdc.SharePoint.SPContentStructure.Common;

namespace DE.Sdc.SharePoint.SPContentStructure.Import
{
    [Serializable]
    [XmlInclude(typeof(Web))]
    [XmlInclude(typeof(Folder))]
    [XmlInclude(typeof(SPContentStructureImport))]
    public abstract class BaseItem<TParent> : HierarchicalElement<TParent>, IBaseItem where TParent : IBaseItem
    {
        protected BaseItem()
        {
            Properties = new ItemProperties(this);

            
        }

        /// <remarks/>
        [XmlArrayItem("Property")]
        public ItemProperties Properties { get; set; }

        [XmlIgnore]
        public bool PropertiesSpecified
        {
            get
            {
                return Properties != null && Properties.Count > 0;
            }
        }

        

        /// <remarks/>
        [XmlAttribute]
        public int Version { get; set; }

        [XmlIgnore]
        public string AbsoluteUrl
        {
            get
            {
                if (GetType() == typeof(SPContentStructureImport))
                    return "";

                if (Parent != null)
                    return Parent.AbsoluteUrl + (String.IsNullOrEmpty(Parent.AbsoluteUrl) ? "" : "/") + (Url ?? Title);

                throw new ArgumentNullException("Can not resolve AbsoluteUrl. No parent specified for '" + Title + "'");
            }
            set
            {
                if (GetType() == typeof(SPContentStructureImport))
                    return;

                if (Parent != null && !AbsoluteUrl.EndsWith(value))
                    AbsoluteUrl = Parent.AbsoluteUrl + value;
            }
        }

        

        /// <summary>
        /// Changes the status a given item to approve
        /// </summary>
        /// <param name="item"></param>
        public void ApproveItem(SPListItem item)
        {
            String title = item.Fields.ContainsField("Title") ? item.Title : item.Name;

            Trace.WriteLine("Try to approve item '" + title + "' with url '" + item.Url + "'.", "ContentStructureImporter:ApproveItem");

            if (item.ModerationInformation != null &&
                item.ModerationInformation.Status == SPModerationStatusType.Pending)
            {
                if (item.File == null)
                {
                    item.ModerationInformation.Status = SPModerationStatusType.Approved;
                    item.ModerationInformation.Comment = "";
                }
                else
                    item.File.Approve("");
            }

            Trace.WriteLine("Item '" + title + "' with url '" + item.Url + "' approved.", "ContentStructureImporter:ApproveItem");
        }

        protected abstract bool IsNewVersion(object sharePointObject);

        protected abstract void SetNewVersion(object sharePointObject);

        /// <summary>
        /// Modifies the properties of an object
        /// </summary>
        /// <param name="objectToModifyProperties">The properties object</param>
        protected void ModifyObjectProperties(object objectToModifyProperties)
        {
            Trace.WriteLine("Try to modify object properties '" + Title + "' with url '" + AbsoluteUrl + "'.", "ContentStructureImporter:ModifyObjectProperties");

            Type objectType = objectToModifyProperties.GetType();

            foreach (Property property in Properties)
            {
                ModifyObjectProperty(objectToModifyProperties, objectType, property.Name, property.Value);
            }

            Trace.WriteLine("Object properties modified '" + Title + "' with url '" + AbsoluteUrl + "'.", "ContentStructureImporter:ModifyObjectProperties");
        }

        private void ModifyObjectProperty(object objectToModifyProperties, Type objectType, string propertyName, string propertyValue)
        {
            PropertyInfo propertyInfo = objectType.GetProperty(propertyName);

            if (propertyInfo == null)
                throw new ContentStructureException("Can not modify property '" + propertyName + "' because it does not exist in the object of type '" + objectType.Name + "'");

            object currentValue = propertyInfo.GetValue(objectToModifyProperties, null);

            if (currentValue != null && currentValue.ToString() != propertyValue ||
                currentValue == null && propertyValue != null)
            {
                object newValue = propertyValue;

                if (propertyInfo.PropertyType.IsEnum)
                    newValue = Enum.Parse(propertyInfo.PropertyType, propertyValue);
                else if (propertyInfo.PropertyType != typeof(string))
                    newValue = Convert.ChangeType(newValue, propertyInfo.PropertyType);

                propertyInfo.SetValue(objectToModifyProperties, newValue, null);
                Trace.WriteLine("Change property '" + propertyName + "' of item '" + Title ?? Url + "'");
            }
        }


        /// <summary>
        /// Adds or updates properties in a given hashtable
        /// </summary>
        /// <param name="propertyKey">Name of the property</param>
        /// <param name="propertyValue">Value of the property</param>
        /// <param name="modifyIfPropertyValueIsNull">Indicates whether the property can be updated with null-values or not</param>
        /// <param name="properties">The property hashtable</param>
        protected static internal void ModifyProperty(string propertyKey, object propertyValue, bool modifyIfPropertyValueIsNull, Hashtable properties)
        {
            if (properties == null)
                return;

            if (!modifyIfPropertyValueIsNull && propertyValue == null)
                return;

            Trace.WriteLine("Try to modify property '" + propertyKey + "' with value '" + propertyValue + "'.", "ContentStructureImporter:ModifyProperty");
            if (properties.ContainsKey(propertyKey))
                properties[propertyKey] = propertyValue;
            else
                properties.Add(propertyKey, propertyValue);
            Trace.WriteLine("Property '" + propertyKey + "' with value '" + propertyValue + "' modified.", "ContentStructureImporter:ModifyProperty");
        }

        public static bool IsExternalUrl(string url)
        {
            return url.StartsWith("/") || url.StartsWith("http") || url.StartsWith(@"\\") || url.StartsWith("#") || url.StartsWith("javascript:") || url.Contains("?");
        }

        internal static SPListItemCollection GetListItems(string[] primaryKeyFields, SPFolder parentFolder, Item itemDefinition)
        {
            if (primaryKeyFields == null)
                return null;
            //Todo find a better way to identify Pri,aries for sp07 
            //Warning import twice will duplicate !
       
           //     throw new ContentStructureException(string.Format("No value for 'primaryKeyFields' attribute for list definition at list with url '{0}' specified.", DeploymentContext.Current.List.ParentWebUrl + DeploymentContext.Current.List.Title));

            SPQuery query = new SPQuery {Folder = parentFolder};
            Hashtable itemProperties = itemDefinition.Properties.GetAsHashtable(null);

            if (itemDefinition.Parent != null && itemDefinition.Parent.FindItemsRecursive)
                query.ViewAttributes = "Scope='RecursiveAll'";

            string primaryKeyFieldsQuery = "";
            for (int i = 0; i < primaryKeyFields.Length; i++)
            {
                string primaryKeyField = primaryKeyFields[i];
                if (primaryKeyField.ToLower() == "title")
                    primaryKeyFieldsQuery += "<Eq><FieldRef Name='Title' /><Value Type='Text'>" + itemDefinition.Title + "</Value></Eq>";
                else
                {
                    string primaryKeyValue = "";
                    if (itemProperties.ContainsKey(primaryKeyField))
                        primaryKeyValue = (string) itemProperties[primaryKeyField];

                    primaryKeyFieldsQuery += "<Eq><FieldRef Name='" + primaryKeyField + "' /><Value Type='Text'>" + primaryKeyValue + "</Value></Eq>";
                }

                if (i > 0)
                    primaryKeyFieldsQuery = "<And>" + primaryKeyFieldsQuery + "</And>";
            }

            query.Query = "<Where>" + primaryKeyFieldsQuery + "</Where>";
            SPListItemCollection items = parentFolder.ParentWeb.GetList(parentFolder.ServerRelativeUrl).GetItems(query);

            //if (items.Count == 0)
            //    throw new ContentStructureImporterException(
            //        "Failed to get unique item by specified list primary key fields.");
            //if (items.Count > 1)
            //    throw new ContentStructureImporterException(
            //        "Multiple items for specified list primary key fields (" + string.Concat(primaryKeyFields) + " found.");
            
            //return items[0];
            return items;
        }

        protected static SPListItemCollection GetListItems(SPFolder parentFolder)
        {
            SPQuery query = new SPQuery {Folder = parentFolder, Query = ""};

            SPListItemCollection items = parentFolder.ParentWeb.Lists[parentFolder.ParentListId].GetItems(query);
            return items;
        }
    }
}